<?php

namespace app\models\jasper;

use Yii;
use yii\base\Model;

/**
 * Jasper物联网接口
 * Class Api
 * @package app\models\jasper
 */
class Api extends Model
{
    const API_VERSION = '1.0'; // 自己定义的接口版本
    /**
     * @var string 服务器
     */
    private $server;
    /**
     * @var string 接口授权
     */
    private $license;
    /**
     * @var string 用户名
     */
    private $username;
    /**
     * @var string 密码
     */
    private $password;
    /**
     * @var SoapClient[] 缓存的SoapClient
     */
    private static $client;
    /**
     * @var integer 消息流水号
     */
    private static $msgid;

    /**
     * @inheritdoc
     * @param string $ispData isp.data
     */
    public function __construct($ispData)
    {
        $json = json_decode($ispData, true);
        $this->server = isset($json['server']) ? $json['server'] : '';
        $this->license = isset($json['license']) ? $json['license'] : '';
        $this->username = isset($json['username']) ? $json['username'] : '';
        $this->password = isset($json['password']) ? $json['password'] : '';
        parent::__construct([]);
    }

    use JasperApiTerminalTrait, JasperApiBillingTrait, JasperApiSmsTrait, JasperApiAlertTrait, JasperApiAccountTrait;
    use JasperApiNetworkAccessTrait, JasperApiEventplanTrait, JasperApiGlobalSimTransferTrait, JasperApiOrderTrait;
    use JasperApiAddRoutableMSISDNTrait, JasperApiEchoTrait;

    /**
     * 调用方法
     * @param string $wsdlType WSDL类型，决定使用的wsdl地址
     * @param string $method 方法名
     * @param array $params 参数
     * @param boolean $withLicense 是否将LicenseKey放到参数里
     * @return \stdClass
     * @throws \Exception
     */
    private function call($wsdlType, $method, $params, $withLicense = true)
    {
        if (!isset(Api::$client[$wsdlType])) {
            /**
             * @see https://cc1.10646.cn/provision/secure/apidoc/ > Technical References > Get WSDL Files
             */
            $wsdl = $this->server . '/ws/schema/' . $wsdlType . '.wsdl';
            $config = [
                'soap_version' => SOAP_1_1,
                'cache_wsdl' => YII_DEBUG ? WSDL_CACHE_NONE : WSDL_CACHE_DISK,
                'user_agent' => Yii::$app->id . '/' . Yii::$app->version,
                'trace' => YII_DEBUG,
            ];
            Api::$client[$wsdlType] = new SoapClient($wsdl, $config);
        }
        $headerNs = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
        $Security = new \stdClass();
        $UsernameToken = new \stdClass();
        $UsernameToken->Username = new \SoapVar($this->username, XSD_STRING, null, null, null, $headerNs);
        $UsernameToken->Password = new \SoapVar($this->password, XSD_STRING, null, null, null, $headerNs);
        $Security->UsernameToken = new \SoapVar($UsernameToken, SOAP_ENC_OBJECT, null, null, null, $headerNs);
        Api::$client[$wsdlType]->__setSoapHeaders(
            new \SoapHeader(
                $headerNs,
                'Security',
                $Security,
                false
            )
        );
        if (!isset($params['messageId'])) {
            $params['messageId'] = $this->nextMsgId();
        }
        if (!isset($params['version'])) {
            $params['version'] = Api::API_VERSION;
        }
        if ($withLicense) {
            $params['licenseKey'] = $this->license;
        }
        try {
            $resp = Api::$client[$wsdlType]->$method($params);
            if (YII_DEBUG) {
                Yii::warning(print_r(Api::$client[$wsdlType]->__getLastRequestHeaders(), true), 'Jasper');
                Yii::warning(print_r(Api::$client[$wsdlType]->__getLastRequest(), true), 'Jasper');
                Yii::warning(print_r(Api::$client[$wsdlType]->__getLastResponseHeaders(), true), 'Jasper');
                Yii::warning(print_r(Api::$client[$wsdlType]->__getLastResponse(), true), 'Jasper');
            }
            return $resp;
         } catch (\Exception $e) {
            Yii::warning(print_r(Api::$client[$wsdlType]->__getLastRequestHeaders(), true), 'Jasper');
            Yii::warning(print_r(Api::$client[$wsdlType]->__getLastRequest(), true), 'Jasper');
            Yii::warning(print_r(Api::$client[$wsdlType]->__getLastResponseHeaders(), true), 'Jasper');
            Yii::warning(print_r(Api::$client[$wsdlType]->__getLastResponse(), true), 'Jasper');
            throw $this->transformException($e);
        }
    }

    /**
     * 返回下一个消息编号
     * @return int
     */
    private function nextMsgId()
    {
        if (Api::$msgid > PHP_INT_MAX) {
            Api::$msgid = 0;
        }
        return Api::$msgid++;
    }

    /**
     * 转换异常信息
     * @param \Exception $e
     * @return \Exception
     */
    private function transformException($e)
    {
        if (preg_match('/^\d+$/', $e->getMessage())) {
            $codeMap = [
                // General (一般问题)
                '10000' => 'Wildcard search is too general (通配符搜索太宽泛)',
                '10001' => 'Maximum size exceeded (超出上限)',
                // Devices (设备)
                '100000' => 'Device error (设备错误)',
                '100100' => 'Device not found. (未找到设备。)', // The ICCID doesn't exist in the caller's account. (ICCID 在呼叫方账户中不存在。)This type of error can occur when you try to use a sandbox account to access a production device. (当您在尝试使用沙箱账户访问生产设备时，可能出现这种错误。)
                '100200' => 'Invalid device change type (设备变更类型无效)',
                '100300' => 'Invalid edit terminal request (编辑终端请求无效)',
                '100400' => 'Invalid device(s) specified (指定的设备无效)',
                '100401' => 'Invalid device ID specified (指定的设备 ID 无效)',
                '100500' => 'Offer not found (未找到服务)',
                '100501' => 'Invalid offer (无效服务)',
                '100510' => 'Campaign not found (未找到活动)',
                '100520' => 'Invalid rate plan (无效资费计划)',
                '100101' => 'Invalid ICCID (无效 ICCID)',
                // Billing (计费)
                '200000' => 'Billing error (计费错误)',
                '200100' => 'Invoice does not exist (账单不存在)',
                '200200' => 'No device usage found (未找到设备用量)',
                // Admin (管理员)
                '300000' => 'Admin error (管理员错误)',
                '300100' => 'Invalid Qcode (无效 Qcode)',
                '300200' => 'Invalid account name (账户名无效)',
                '300201' => 'Account name not unique (账户名不唯一)',
                '300300' => 'Invalid email (无效电邮)',
                '300301' => 'Email not unique (电子邮件不唯一)',
                // API
                '400000' => 'API error (API 错误)',
                '400100' => 'Invalid license key. (许可密钥无效。)', // Please use a valid API license key. (请使用有效的 API 许可密钥。) Make sure you're using the correct license key for the current environment. (确保您使用的是适合当前环境的正确许可密钥。)Sometimes people mistakenly use their sandbox license key on the production site or vice versa. (有时人们会错误地在生产站点上使用其沙箱许可密钥，反之亦然。)
                '400101' => 'License has exceeded the rate limit for API calls. (许可已超出 API 调用的资费限制。)',
                '400200' => 'Security validation error. (安全验证错误。)', // Your username or password is incorrect. (用户名或密码不正确。) Make sure you're using the right credentials for the current environment. (确保您使用的是适合当前环境的正确凭据。)Sometimes people mistakenly use their apitest credentials on the production site or vice versa. (有时人们会错误地在生产站点上使用其 apitest 凭据或相反。)
                '400201' => 'Access violation error. (访问违例错误。)', // Please check your login; you may be trying to access a device that belongs to a different account or service provider. (请检查您的登录信息；您尝试访问的设备可能属于其他账户或运营商。) This error can happen if the caller has accounts with multiple operators and they're using the login from the wrong operator. (如果呼叫方有多个运营商账户，而他们从错误的运营商登录，则可能出现此错误。)
                '400300' => 'System internal error (系统内部错误)',
                '400400' => 'Too many entries (条目太多)',
                '400500' => 'Invalid ICCID (无效 ICCID)',
                // SMS (短信)
                '500000' => 'SMS error (SIM 卡错误)',
                '500100' => 'From Date greater than To Date (“起始日期”迟于“结束日期”)',
                '500200' => 'Too many entries (条目太多)',
                '500300' => 'SMS message not found (未找到 SIM 卡消息)',
                '500400' => 'Message text null (短信内容为空)',
                '500500' => 'Message text greater than 160 characters (短信内容超过 160 个字符)',
                '500600' => 'Send message failure (发送消息失败)',
                '500700' => 'Device not active or is suspended (设备未启用或已中止)',
                '500800' => 'Decoding error (解码错误)',
                '500900' => 'Decoded message text null (解码的短信内容为空)',
                'Network Access Config (also called a Communication Plan) (网络访问配置，也称为通信计划)',
                '800000' => 'Network Access Config not found (未找到网络访问配置)',
                '800100' => 'Invalid edit Network Access Config request (无效的编辑网络访问配置请求)',
                // Messaging (消息)
                '900100' => 'No active session (没有启用的会话)',
                '900101' => 'Protocol not supported (协议不受支持)',
                '900102' => 'Invalid port (无效端口)',
                '900103' => 'Communication failed (通信失败)',
                '900104' => 'Communication timeout (通信超时)',
                // Accounts (账户)
                '1000100' => 'Account not found (未找到账户)',
                '1000400' => 'Too many accounts (账户太多)',
                '1000500' => 'Service provider not found (未找到运营商)',
                '1000501' => 'Could not create account (无法创建账户)',
                '1000502' => 'Internal Error - default trial communication plan not found for service provider (内部错误 - 未找到运营商的默认试用通信计划)',
                '1000503' => 'Internal Error - default trial rate plan not found for service provider (内部错误 - 未找到运营商的默认试用资费计划)',
                '1000504' => 'Account already exists (账户已存在)',
                '1000505' => 'Invalid region ID (无效地区 ID)',
                '1000506' => 'Default communication plan not found for service provider (未找到运营商的默认通信计划)',
                '1000507' => 'Operator Account ID is not unique; multiple accounts found. (运营商账户 ID 不唯一；找到多个账户。)',
                '1000508' => 'Cannot transfer device to this account (无法将设备转移到此账户)',
                '1000509' => 'Cannot transfer device to the same account (无法将设备转移到同一账户)',
                '1000510' => 'Cannot transfer device when one of the accounts is an inventory account (当其中一个账户是库存账户时，无法转移设备)',
                // Edit Device Rating (编辑设备资费)
                '1100100' => 'Edit device rating error (编辑设备资费错误)',
                '1100200' => 'Term end date must be greater than term start date (期限结束日期必须晚于期限开始日期)',
                '1100300' => 'Renewal rate plan is blank (续约资费计划为空)',
                '1100400' => 'Renewal rate plan is not valid for the device (续约资费计划对设备无效)',
                '1100500' => 'Renewal mode is not valid (续约模式无效)',
                '1100600' => 'Access denied. (拒绝访问。)Cannot edit term start and end dates (无法编辑期限开始日期和结束日期)',
                '1100700' => 'Failed to remove rate plan from queue (从队列中删除资费计划失败)',
                '1110100' => 'Add device renewal error (添加设备续约错误)',
                // LinePayStatus
                '1120100' => 'Edit LinePayStatus error (编辑 LinePayStatus 错误)',
                '1120101' => 'Invalid LinePayStatus parameter (无效 LinePayStatus 参数)',
                '1120102' => 'Invalid operation for SimStatus is Purged (针对 SIM 卡状态的无效操作已清除)',
                '1120104' => 'No terminals associated with Operator Account ID (不存在与运营商账户 ID 关联的终端)',
                // Event Plans (事件计划)
                '1200100' => 'Invalid event name (无效事件名称)',
                '1200200' => 'Event instance not found (未找到事件实例)',
                '1200300' => 'Event instance started already (事件实例已开始)',
                // Connection Manager (连接管理器)
                '1300100' => 'Configuration file not found (未找到配置文件)',
                '1300200' => 'Failed to upload diagnostics data (上传诊断数据失败)',
                '1300300' => 'Invalid sequence number (无效序列号)',
                // Users (用户)
                '1400100' => 'Try a different user name. (请尝试其他用户名。)',
                '1400101' => 'Customer not found (未找到客户)',
                '1400102' => 'Role not found (未找到角色)',
                '1400103' => 'Could not create user (无法创建用户)',
                '1400104' => 'Password invalid (密码无效)',
                '1400105' => 'Invalid authentication token (身份验证令牌无效)',
                '1400106' => 'Not authorized to create this role (没有创建此角色的权限)',
                '1400107' => 'Peered user information must be specified (必须指定对等用户信息)',
                '1400108' => 'Account not peered (账户不对等)',
                '1400109' => 'User not found (未找到用户)',
                '1400110' => 'Could not delete user (无法删除用户)',
                '1400111' => 'Could not reset user password (无法重置用户密码)',
                // Passwords (密码)
                '1401001' => 'Illegal characters in password (密码中存在非法字符)',
                '1401002' => 'Password length less than the required minimum (密码长度小于要求长度)',
                '1401003' => 'Password length more than the maximum (密码长度超过最大长度)',
                '1401004' => 'Contains fewer alphabetic characters than the required minimum (包含的字母字符数少于要求的最低数量)',
                '1401005' => 'Contains more alphabetic characters than the maximum (包含的字母字符数多于允许的最大数量)',
                '1401006' => 'Contains fewer numeric characters than the required minimum (包含的数字字符数少于要求的最低数量)',
                '1401007' => 'Contains more numeric characters than the maximum (包含的数字字符数多于允许的最大数量)',
                '1401008' => 'Contains fewer special characters than the required minimum (包含的特殊字符数少于要求的最低数量)',
                '1401009' => 'Contains more special characters than the maximum (包含的特殊字符数多于允许的最大数量)',
                '1401010' => 'Password matches one of the previous passwords (密码与以往密码相同)',
                '1401011' => 'Contains fewer uppercase characters than the required minimum (包含的大写字符数少于要求的最低数量)',
                // Vault
                '1500100' => 'IMSI not found (未找到 IMSI)',
                '1500200' => 'Start Date is not before End Date (开始日期不早于结束日期)',
                // Rate Plans (资费计划)
                '1600100' => 'Invalid rate plan or not a wholesale rate plan (无效资费计划或者不是批发资费计划)',
                // Communication Plans (通信计划)
                '1700100' => 'Invalid communication plan (无效通信计划)',
                '1700101' => 'Invalid APN (无效 APN)',
                '1700102' => 'Invalid IP address (无效 IP 地址)',
                // Global SIM (全球 SIM 卡)
                '1800100' => 'Global SIM unknown issues (全球 SIM 卡未知问题)',
                '1800101' => 'Global SIM not found (未找到全球 SIM 卡)',
                '1800102' => 'Global partner not found (未找到全球合作伙伴)',
                '1800103' => 'Invalid GlobalSimTransferId in the request (请求中的 GlobalSimTransferId 无效)',
                '1800104' => 'Invalid transfer request. (无效转移请求。)', // Primary ICCID hasn't been transferred to any partner. (主要 ICCID 未转移给任何合作伙伴。)
                '1800105' => 'ICCID already has a global transfer pending. (ICCID 已经有待处理的全球转移。)',
                '1800106' => 'ICCID does not have any pending global transfer requests. (ICCID 没有任何待处理的全球转移请求。)',
                '1800107' => 'Global alliance not found (未找到全球联盟)',
                '1800108' => 'Target operator does not exist. (目标运营商不存在。)',
                '1800109' => 'Target Global SIM subscription information not found. (未找到目标全球 SIM 卡订购信息。)',
                '1800110' => 'Account mapping not defined for Global SIM transfer. (未定义全球 SIM 卡转移的账户映射。)',
                '1800111' => 'Global SIM is in invalid state for Global SIM transfer. (全球 SIM 卡处于无效状态，无法进行全球 SIM 卡转移。)',
                '1800112' => 'Target subscription is already in use.(目标订购已在使用中。)',
                '1800113' => 'G&D IMSI swap failed for the Global SIM. (全球 SIM 卡的 G&D IMSI 交换失败。)',
                '1800114' => 'Cisco Jasper IMSI swap failed for the Global SIM. (全球 SIM 卡的 Cisco Jasper IMSI 交换失败。)',
                '1800115' => 'Global SIM not live (全球 SIM 卡未启用)',
                '1800116' => 'Global SIM transfer can not be cancelled for the SIM at this stage. (在此阶段，不能取消该 SIM 卡的全球 SIM 卡转移。)',
                '1800132' => 'Invalid request; duplicate messageId used (无效请求；使用了重复的 messageId)',
            ];
            if (isset($codeMap[$e->getMessage()])) {
                return new \Exception($codeMap[$e->getMessage()], $e->getMessage());
            }
            return new \Exception('Unknown exception.', $e->getMessage());
        } else {
            return $e;
        }
    }
}
